#pragma once 

#include <iostream>
#include <pthread.h>
#include <queue>

#define MAX 5

using namespace std;

template<class T>
class threadpool
{
  private:
    int max;
    queue<T> q;
    pthread_mutex_t lock;
    pthread_cond_t cond;
    pthread_mutex_t lock1;
    pthread_cond_t cond1;
  public:
    threadpool(const int& _a=MAX):max(_a)
    {
      pthread_mutex_init(&lock,nullptr);
      pthread_cond_init(&cond,nullptr);
      pthread_mutex_init(&lock1,nullptr);
      pthread_cond_init(&cond1,nullptr);
    }
    ~threadpool()
    {
      pthread_mutex_destroy(&lock);
      pthread_cond_destroy(&cond);
      pthread_mutex_destroy(&lock1);
      pthread_cond_destroy(&cond1);
    }
    static void* Run(void* agd)
    {
      threadpool<T>* tp=(threadpool<T>*)agd;
      pthread_detach(pthread_self());
      while(true)
      {
        tp->Lock();
        while(tp->Empty())
        {
          tp->Wait();
        }
        T t;
        tp->pop(t);
        tp->Unlock();
        t.Run();
        tp->Signal();
      }
    }
    void Signal()
    {
      pthread_cond_signal(&cond);
      pthread_cond_signal(&cond1);
    }
    void Wait()
    {
      pthread_cond_wait(&cond,&lock);
    }
    bool Empty()
    {
      return q.size()==0;
    }
    void Lock()
    {
        pthread_mutex_lock(&lock);
    }
    void Unlock()
    {
        pthread_mutex_unlock(&lock);
    }
    void InitPd()
    {
      pthread_t se;
      for(int i=0;i<max;i++)
      {
          pthread_create(&se,nullptr,Run,this);
      }
    }

    void* push(const T& in)
    {
        pthread_mutex_lock(&lock1);
        while(q.size()/max>10)
        {
          pthread_cond_wait(&cond1,&lock1);
        }
          q.push(in);
        pthread_mutex_unlock(&lock1);
        pthread_cond_signal(&cond1);
        pthread_cond_signal(&cond);
    }
    void pop(T& out)
    {
        out=q.front();
        q.pop();
    }

};
